"use strict";

var router = require("router"),
    TokenService = require("router").TokenService,
    mongo = require('./core/mongoservice'),
    ObjectId = require('mongodb').ObjectID,
    Q = require('q');

function UserService() {
    console.log("UserService constructor");
}
UserService.prototype = Object.create(TokenService.prototype);
UserService.prototype.constructor = UserService;

UserService.prototype.save = function (request, response, promise) {
    return promise.then(function (self) {
        //self.identify(request, response);
        return self.mongoSave(request.body).then(function (user) {
            return response.send({ request: request, content: user });
        });
    });
};

UserService.prototype.getById = function (request, response, promise) {
    return promise.then(function (self) {
        return self.mongoGetPageById(request.params.userId).then(function (user) {
            return response.send({ request: request, content: user });
        });
    });
};

UserService.prototype.getFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);
        var userId = request.payload.user.id;
        return self.mongoGetPageById(userId).then(function (user) {
            return response.send({ request: request, content: user });
        });
    });
};

UserService.prototype.getByLogin = function (request, response, promise) {
    return promise.then(function (self) {
        return self.mongoGetPageByLogin(request.params.login).then(function (user) {
            return response.send({ request: request, content: user });
        });
    });
};

UserService.prototype.getListStartsWithLogin = function (request, response, promise) {
    return promise.then(function (self) {

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);
        var query = request.query.login;

        return self.mongoGetListStartsWithLogin(query, { skip: skip, limit: limit}).then(function (users) {
            return response.send({ request: request, content: users });
        });
    });
};

UserService.prototype.getEmployersFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);
        var userId = request.payload.user.id;
        return self.mongoGetEmployers(userId).then(function (users) {
            return response.send({ request: request, content: users[0] });
        });
    });
};

UserService.prototype.getSubscriptionsFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);
        
        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);
        var userId = request.payload.user.id;

        return self.mongoGetSubscriptions(userId, { skip: skip, limit: limit}).then(function (users) {
            return response.send({ request: request, content: users });
        });
    });
};

UserService.prototype.getFullById = function (request, response, promise) {
    return promise.then(function (self) {
        //self.identify(request, response);

        var userId = request.params.userId;

        return self.mongoGetById(userId).then(function (user) {
            user.password = "";
            return response.send({ request: request, content: user });
        });
    });
};

UserService.prototype.getFullFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var userId = request.payload.user.id;

        return self.mongoGetById(userId).then(function (user) {
            user.password = "";
            return response.send({ request: request, content: user });
        });
    });
};

UserService.prototype.remove = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var userId = request.body.userId;

        return self.mongoRemove(userId).then(function (user) {
            return response.send({ request: request, content: user });
        });
    });
};

UserService.prototype.passwordRecovery = function (request, response, promise) {
    return promise.then(function (self) {

        var user = request.body;
        
        return self.mongoGetByLogin(user.login).then(function (user) {
            return require("./emailservice").sendRecovery(user).then(function () {
                return response.send({ request: request, content: user });
            });
        });
    });
};

/*-------------------------------------------------------*/
/*mongo daf*/
UserService.prototype.mongoInsert = function (user) {
    if (!user) throw new Error("user is not defined.");
    if (user._id) throw new Error("user already has id: " + user._id);
    var self = this;

    user.dates.creation = new Date();

    return require("./referenceservice").mongoIsLoginAuthorized(user.login).then(function (res) {
        return self.validate(user).then(function (user) {
            return mongo.insert('users', user).then(function (users) {                
                return users[0];
            }).then(function (user) {
                return self.mongoLocalizeUser(user).then(function () {
                    return require("./referenceservice").mongoInsert(user.login, user._id, "user").then(function () {
                        return self.mongoGetById(user._id);
                    });
                });
            });
        });
    });
};

UserService.prototype.mongoUpdate = function (user) {
    if (!user) throw new Error("user is not defined.");
    if (!user._id) throw new Error("user id is not defined.");
    var self = this;

    user._id = new ObjectId(user._id);
    return self.mongoGetById(user._id).then(function (oldUser) {
        user.login = oldUser.login; //login can not be modified.
        if (user.password == "") user.password = oldUser.password; //password is empty if it hasn't been modified (hidden)
        return self.validate(user).then(function (user) {
            return self.mongoUnlocalizeUser(user._id).then(function () {
                return mongo.update('users', { _id: user._id }, user).then(function () {
                    return self.mongoLocalizeUser(user).then(function () {
                        return self.mongoGetById(user._id);
                    });
                });
            });
        });
    });
};

UserService.prototype.mongoSave = function (user) {
    if (!user) throw new Error("user is not defined.");
    var self = this;

    if (user._id) return self.mongoUpdate(user);
    else return self.mongoInsert(user); 
}

UserService.prototype.mongoRemove = function (userId) {
    if (!userId) throw new Error("userId is not defined.");
    var self = this;

    return self.mongoGetById(userId).then(function (user) {
        return mongo.remove('users', { id: user._id }).then(function () { //move to archive
            return mongo.insert('users.archives', user).then(function () {
                return mongo.findOne('users.reports', { "user.id": user._id }).then(function (report) {
                    return mongo.remove('users.reports', { "user.id": user._id }).then(function () {  //move report to archive
                        return mongo.insert('users.reports.archives', report).then(function () {
                            return require("./referenceservice").mongoRemove(user._id, "user");
                        });
                    });
                });
            });
        });
    });
};

UserService.prototype.mongoGetById = function (userId) {
    if (!userId) throw new Error("userId is not defined.");

    return mongo.findOne('users', { _id: new ObjectId(userId) });
};

UserService.prototype.mongoGetPageById = function (userId) {
    var self = this;
    return self.mongoGetById(userId).then(function (user) {
        return self.createUserPage(user);
    });
};

UserService.prototype.mongoGetByLogin = function (login) {
    if (!login) throw new Error("login is not defined.");

    return mongo.findOne('users', { login: login });
};

UserService.prototype.mongoGetByLoginAndPassword = function (login, password) {
    if (!login) throw new Error("login is not defined.");
    if (!password) throw new Error("Password is empty.");

    return mongo.findOne('users', { login: login, password: password });
};

UserService.prototype.mongoGetPageByLogin = function (login) {
    var self = this;
    return self.mongoGetByLogin(login).then(function (user) {
        return self.createUserPage(user);
    });
};

UserService.prototype.mongoGetEmployers = function (userId) {
    if (!userId) throw new Error("userId is not defined.");

    var opts = {};
    opts.sort = "name";
    return mongo.find('shops', { "users.employees.id": new ObjectId(userId) }, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (users) {
            if (users.length == 0) throw new Error('User is not an employee.');
            return users;
        })
    });
};

UserService.prototype.mongoGetSubscriptions = function (userId, options) {
    if (!userId) throw new Error("userId is not defined.");
    options = options || {};

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = {"name": 1};

    return mongo.find('shops', { "users.subscribers.id": new ObjectId(userId) }, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray');
    });
};

UserService.prototype.mongoGetListStartsWithLogin = function (query, options) {
    options = options || {};

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = {"login": 1};

    opts.limit = opts.limit || 20;

    var regex = new RegExp("^" + query, "ig");

    return mongo.find('users', { login: { $regex: regex} }, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray');
    });
};

UserService.prototype.mongoGetListByLogin = function (login) {
    return mongo.findOne('users', { login: login} );
};

UserService.prototype.mongoGetNearShops = function (userId) {
    if (!userId) throw new Error("userId is not defined.");

    return mongo.find('shops', { "users.near.id": new ObjectId(userId) }).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray');
    });
};


UserService.prototype.mongoLocalizeUser = function (user) {
    if (!user) throw new Error("user is not defined.");
    if (!user._id) throw new Error("user id is not defined.");

    var self = this;

    var options = {
        spherical: true,
        maxDistance: user.config.distanceM / 0.6371,
        distanceMultiplier: 0.6371
    };

    return mongo.geoNear('shops', user.location.loc.coordinates[0], user.location.loc.coordinates[1], options).then(function (doc) {
        var promises = [];
        doc.results.forEach(function (item) {
            var shp = { id: item.obj._id, distanceM: item.dis };
            var usr = { id: user._id, distanceM: item.dis };

            var promise = mongo.update('shops', { _id: shp.id }, { $push: { "users.near": usr} }).then(function () {
                return mongo.update('alerts', { "shop.id": shp.id }, { $push: { "users.near": usr} }, { multi: true }).then(function () {
                    return mongo.update('users', { _id: usr.id }, { $push: { "shops.near": shp} });
                });
            });
            promises.push(promise);
        });
        return Q.all(promises);
    }).then(function () {
        return self.mongoGetById(user._id).then(function (user) {
            return require("./userreportservice").mongoInsert(user).then(function () {
                return user;
            });
        });
    });
};

UserService.prototype.mongoUnlocalizeUser = function (userId) {
    if (!userId) throw new Error("user id is not defined.");
    var self = this;

    userId = new ObjectId(userId);
    var usr = { id: userId };

    return mongo.update('shops', { }, { $pull: { "users.near": usr} }, { multi: true }).then(function () {
        return mongo.update('alerts', { }, { $pull: { "users.near": usr} }, { multi: true }).then(function () {
            return mongo.update('users', { _id: usr.id }, {$set: {"shops.near": []}});
        });
    }).then(function () {
        return self.mongoGetById(userId).then(function (user) {
            return require("./userreportservice").mongoInsert(user).then(function () {
                return user;
            });
        });
    });
}

/*--------------------------------------------*/
/*Common*/
UserService.prototype.createUserPage = function (user) {
    if (!user) throw new Error("user is not defined.");
    var self = this;

    //hide password
    user.password = "";
    //overwrite users & shops
    user.shops = {
        subscriptions: {
            total: user.shops.subscriptions.length
        },
        near: {
            total: user.shops.near.length
        },
        employers: {
            total: user.shops.employers.length
        }
    };

    return user;
}

UserService.prototype.validate = function (user) {
    return Q.fcall(function () {
        if (!user) throw new Error("user is not defined.");

        if (!user.login || user.login.length == 0) throw new Error("Login is empty");
        if (!/(^[\w|[0-9]|áàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ\']{3,}$)/gi.test(user.login)) throw new Error("Bad login format.");

        if (!user.password || user.password.length == 0) throw new Error("Password is empty.");
        if (!/(^[\w|[0-9]|áàâäãåçéèêëíìîïñóòôöõúùûüýÿæœÁÀÂÄÃÅÇÉÈÊËÍÌÎÏÑÓÒÔÖÕÚÙÛÜÝŸÆŒ\']{3,}$)/gi.test(user.password)) throw new Error("Bad password format.");

        if (!user.contact.email || user.contact.email.length == 0) throw new Error("Email is empty.");
        if (!/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(user.contact.email)) throw new Error("Bad email format");

        if (!user.location) throw new Error("Location is empty.");
        if (!user.location.address) throw new Error("Location is empty.");
        if (user.location.loc.coordinates.length != 2) throw new Error("Address is not well defined.");
        return user;
    });
};

exports = module.exports = new UserService();